#ifndef __STDSTRATEGO_H
#define __STDSTRATEGO_H

#define cast(typename, id)     replace(`i`, id, replace(`int`, typename, `(int) i`))

code replace(code sought, code replacement, code searched);
code rewrite(code sought, code replacement, code searched);
code equals(code lhs, code rhs);

code declare(code typename, code id);
code declare1d(code typename, code id, code i);
code declare2d(code typename, code id, code i, code j);
code declarestatic(code typename, code id);

code basetype(code typename);
code addpointer(code typename);
code addrpointer(code typename);
code strippointer(code typename);
code ifconversion(code locals, code body);
code scramblevariables(code codes, code unique);
code scrambleextravariables(code codes, code unique, code vars);

#define DATACLASS_INTEGER 0
#define DATACLASS_FLOAT   1

stratego code dataclass(code a)
{
  rules
    main:
      TypeName(spec, None) -> <main>(spec)
    
    main:    
      TypeSpec(_, Char, _) -> |[0]|
    
    main:
      TypeSpec(_, Int, _) -> |[0]|
      
    main:
      TypeSpec(_, _, l) -> |[0]|
      where (<elem>(Char, l) + <elem>(Int, l))
      
    main:    
      TypeSpec(_, Float, _) -> |[1]|
           
    main:
      TypeSpec(_, Double, _) -> |[1]|
                               
    main:
      TypeSpec(_, _, l) -> |[1]|
      where (<elem>(Float, l) + <elem>(Double, l))
}

stratego code len(code a)
{
  rules
    main:
      ArrayInit(xs) -> IntConst(<length; int-to-string>(xs))
}

stratego code pop_back(code a)
{
  rules
    main:
      ArrayInit(xs) -> ArrayInit(<reverse; Tl; reverse>(xs))
}

stratego code id(code a)
{
  strategies
    id-strategy = id
}

stratego code makeid(code a, code b)
{
  rules
    main:
      (t1#(xs1), t2#(xs2)) -> Id(<conc; concat-strings>(xs1, xs2))
}

stratego code iddrop(code a, code num)
{
  rules
    main:
      (t#(xs), IntConst(n)) ->
      Id(res)
      where (<string-to-int>(n) => inum;
             <Hd; string-as-chars(drop(!inum))>(xs) => res)
}

stratego code idleave(code a, code num)
{
  rules
    main:
      (t#(xs), IntConst(n)) ->
      Id(res)
      where (<Hd; strlen>(xs) => len;
             <string-to-int>(n) => inum;
             <subti>(len, inum) => itake;
             <Hd; string-as-chars(take(!itake))>(xs) => res)
}

stratego code deref(code a)
{
  rules
    main:
      a -> Deref(a)
}

stratego code replacedeep(code src, code rplc, code body)
{
  strategies
    main =
      <alltd(ReplaceDeep)>(body)
  
    ReplaceDeep:
      src -> rplc
}

stratego code idtostring(code i)
{
  rules
    main:
      Id(a) -> StringLit([<double-quote>(a)])
}

code addpromote(code body)
{
  strategies
    main = 
      alltd(AddPromote)

  rules

  AddPromote:
    Compound(decls, stats) -> Compound(<all(AddPromote)>(decls), stats)
    where not(!decls; ?[])

  AddPromote:
    Declaration2(t@TypeSpec(a, b, c),i) ->
    Declaration2(DeclSpec([Promote], t), i)

  AddPromote:
    Declaration2(DeclSpec(d, t), i) ->
    Declaration2(DeclSpec([Promote|d], t), i)

  AddPromote:
    FunDef(t@TypeSpec(_, _, _), i, b) ->
    FunDef(DeclSpec([Promote], t), i, b)

  AddPromote:
    FunDef(DeclSpec(d, t), i, b) ->
    FunDef(DeclSpec([Promote|d], t), i, b)

  AddPromote:
    StrategoDef(t@TypeSpec(_, _, _), i, b) ->
    StrategoDef(DeclSpec([Promote], t), i, b)

  AddPromote:
    StrategoDef(DeclSpec(d, t), i, b) ->
    StrategoDef(DeclSpec([Promote|d], t), i, b)
}

code addwhile(code body)
{
  rules
    main:
      a -> While(Id("__unk"), a)
}

code removewhile(code body)
{
  strategies
    main = 
      topdown(try(RemoveCompound))
      ; RemoveWhile
      
    RemoveWhile:
      While(Id("__unk"), a) -> a
      
    RemoveWhile:
      For(EmptyExp, Id("__unk"), EmptyExp, a) -> a
      
    RemoveWhile:
      DoWhile(a, Id("__unk")) -> a

    RemoveCompound:
      Compound([], [s]) -> s
}

code reduceit(code body, int resext)
{
  return removewhile(reduce(addwhile(body), resext));
}

#endif /* __STDSTRATEGO_H */
